Windows 計劃任務
ok-ef 提供了 日常任務外部命令 和內建計劃任務功能。
如果想要更高的自由度,可以使用 Windows 計劃任務。
1. 從模版建立 pre-hook 和 post-hook 檔案
下面的 终末地.pre.ps1 用於執行前清理舊日誌,並可選地提前啟動遊戲等待熱更新。修改路徑後儲存;示例相容系統自帶的 Windows PowerShell 5.1:
$ErrorActionPreference = "Stop"
$efDir = "C:\Program Files\Hypergryph Launcher\games\Endfield Game"
$okefDir = "C:\ok-ef"
$workingDir = Join-Path $okefDir "data\apps\ok-ef\working"
$hotUpdateMinutes = 5 # 设为 0 可跳过提前启动
if (-not (Test-Path -LiteralPath $workingDir -PathType Container)) {
throw "ok-ef 工作目录不存在: $workingDir"
}
$logDir = Join-Path $workingDir "logs"
if (Test-Path -LiteralPath $logDir -PathType Container) {
$logs = @(Get-ChildItem -LiteralPath $logDir -File)
if ($logs.Count -gt 15) {
$cutoff = (Get-Date).AddDays(-30)
$logs | Where-Object { $_.CreationTime -lt $cutoff } | Remove-Item -Force
}
$currentLog = Join-Path $logDir "ok-script.log"
if (Test-Path -LiteralPath $currentLog -PathType Leaf) {
$timestamp = (Get-Item -LiteralPath $currentLog).CreationTime.ToString("yyyy-MM-dd.HH-mm-ss")
Move-Item -LiteralPath $currentLog -Destination "$currentLog.$timestamp.log" -Force
}
}
if ($hotUpdateMinutes -gt 0) {
$gameExe = Join-Path $efDir "Endfield.exe"
if (-not (Test-Path -LiteralPath $gameExe -PathType Leaf)) {
throw "游戏程序不存在: $gameExe"
}
Start-Process -FilePath $gameExe -WorkingDirectory $efDir
Start-Sleep -Seconds ($hotUpdateMinutes * 60)
Get-Process -Name "Endfield" -ErrorAction SilentlyContinue | Stop-Process -Force
}
# 在这里加入自定义代码(例如消息通知)
下面的 终末地.post.ps1 等待任務退出,超時後結束相關程序,並歸檔日誌與截圖。$maxMinutes 包含前置指令碼的熱更新時間:
$okefDir = "C:\ok-ef"
$workingDir = Join-Path $okefDir "data\apps\ok-ef\working"
$maxMinutes = 44
$hotUpdateMinutes = 5
$deadline = (Get-Date).AddMinutes([Math]::Max(1, $maxMinutes - $hotUpdateMinutes))
# `cmd /c start` 非阻塞,给 ok-ef 和游戏进程预留启动时间。
Start-Sleep -Seconds 5
do {
$gameProcesses = @(Get-Process -Name "Endfield" -ErrorAction SilentlyContinue)
$okefProcesses = @(Get-Process -Name "ok-ef" -ErrorAction SilentlyContinue)
if ($gameProcesses.Count -eq 0 -and $okefProcesses.Count -eq 0) { break }
Start-Sleep -Seconds 1
} while ((Get-Date) -lt $deadline)
$timedOut = $gameProcesses.Count -gt 0 -or $okefProcesses.Count -gt 0
if ($timedOut) {
$gameProcesses | Stop-Process -Force
$okefProcesses | Stop-Process -Force
$message = "执行超时;已终止仍在运行的游戏或 ok-ef 进程。"
} else {
$message = "执行结束。"
}
$logPath = Join-Path $workingDir "logs\ok-script.log"
$version = ""
if (Test-Path -LiteralPath $logPath -PathType Leaf) {
$logLines = Get-Content -LiteralPath $logPath -Encoding UTF8
$summaryPatterns = @("已完成的任务列表", "已失败的任务列表", "已跳过的任务列表", "未处理的任务列表", "当前失败的任务")
foreach ($pattern in $summaryPatterns) {
$line = $logLines | Select-String -Pattern $pattern | Select-Object -Last 1
if ($null -ne $line) { $message += "`n`n$line" }
}
$versionLine = $logLines | Select-String -Pattern "app_version:([^,]+)" | Select-Object -Last 1
if ($null -ne $versionLine -and $versionLine -match "app_version:([^,]+)") {
$version = " " + $matches[1]
}
$timestamp = (Get-Item -LiteralPath $logPath).CreationTime.ToString("yyyy-MM-dd.HH-mm-ss")
Move-Item -LiteralPath $logPath -Destination "$logPath.$timestamp.log" -Force
}
$screenshotDir = Join-Path $workingDir "screenshots"
$backupDir = Join-Path $workingDir "screenshots.backup"
if (Test-Path -LiteralPath $screenshotDir -PathType Container) {
$screenshots = @(Get-ChildItem -LiteralPath $screenshotDir -File -ErrorAction SilentlyContinue)
if ($screenshots.Count -gt 0) {
New-Item -ItemType Directory -Path $backupDir -Force | Out-Null
$screenshots | Move-Item -Destination $backupDir -Force
}
}
# 在这里加入自定义代码(例如使用 $message 和 $version 发送通知)
2. 建立計劃任務檔案並匯入
計劃任務模版如下:
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.3" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<RegistrationInfo>
<SecurityDescriptor></SecurityDescriptor>
</RegistrationInfo>
<Triggers>
<CalendarTrigger>
<StartBoundary>2026-01-01T04:00:00</StartBoundary>
<Enabled>true</Enabled>
<ScheduleByDay>
<DaysInterval>1</DaysInterval>
</ScheduleByDay>
</CalendarTrigger>
</Triggers>
<Principals>
<Principal id="Author">
<LogonType>InteractiveToken</LogonType>
<RunLevel>HighestAvailable</RunLevel>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>true</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
<AllowHardTerminate>true</AllowHardTerminate>
<StartWhenAvailable>false</StartWhenAvailable>
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>
<IdleSettings>
<StopOnIdleEnd>true</StopOnIdleEnd>
<RestartOnIdle>false</RestartOnIdle>
</IdleSettings>
<AllowStartOnDemand>true</AllowStartOnDemand>
<Enabled>true</Enabled>
<Hidden>false</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle>
<DisallowStartOnRemoteAppSession>false</DisallowStartOnRemoteAppSession>
<UseUnifiedSchedulingEngine>true</UseUnifiedSchedulingEngine>
<WakeToRun>false</WakeToRun>
<ExecutionTimeLimit>PT0S</ExecutionTimeLimit>
<Priority>7</Priority>
</Settings>
<Actions Context="Author">
<Exec>
<Command>powershell.exe</Command>
<Arguments>-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "终末地.pre.ps1"</Arguments>
<WorkingDirectory>PLACEHOLDER_PATH_TO_PRE_SCRIPT_DIRECTORY</WorkingDirectory>
</Exec>
<Exec>
<Command>cmd</Command>
<Arguments>/c start "" "ok-ef.exe" -t 1 -e</Arguments>
<WorkingDirectory>PLACEHOLDER_PATH_TO_OK_EF_APP_DIRECTORY</WorkingDirectory>
</Exec>
<Exec>
<Command>powershell.exe</Command>
<Arguments>-NoProfile -ExecutionPolicy Bypass -WindowStyle Hidden -File "终末地.post.ps1"</Arguments>
<WorkingDirectory>PLACEHOLDER_PATH_TO_POST_SCRIPT_DIRECTORY</WorkingDirectory>
</Exec>
</Actions>
</Task>
複製內容到新 xml 檔案,進行下列修改並儲存:
- 修改
PLACEHOLDER_PATH_TO_OK_EF_APP_DIRECTORY為 ok-ef app 的絕對路徑。 - 修改
PLACEHOLDER_PATH_TO_PRE_SCRIPT_DIRECTORY為终末地.pre.ps1檔案所在位置。 - 修改
PLACEHOLDER_PATH_TO_POST_SCRIPT_DIRECTORY為终末地.post.ps1檔案所在位置。
-t 1 表示執行 src/config.py 中 onetime_tasks 的第 1 項,即日常任務。列表順序變化時編號也會變化;改成其他編號前應先核對當前註冊順序。-e 表示任務結束後退出 ok-ef。
啟動時自動校正索引:通過 ok-ef 內建「計劃任務」功能建立的任務(GUI 中「計劃任務」標籤頁),其
-t引數儲存在configs/schedule_tasks_cache.json與 Windows 計劃任務中。應用每次啟動時會讀取快取裡本應用(\ok-ef\)的任務,用其名稱對照當前onetime_tasks順序,自動把-t改寫為正確的當前索引,並同步更新 Windows 計劃任務。因此重排onetime_tasks後無需手動改編號——下次啟動即自動校正;若本次是由計劃任務觸發的,啟動瞬間也會先校正再執行,保證執行到正確的任務。其餘 ok-* 應用(如\ok-gf2\)的任務不會被改動。
使用 Win + R 執行 taskschd.msc 開啟計劃任務程式。點選 操作 > 导入任务 加入上述 xml 檔案。修改 名称 後點擊 确认。
(計劃任務建立後不能重新命名和移動,如果想要修改,可以右鍵任務刪除後重新匯入。上述 xml 檔案匯入後就不再需要,可以刪除。)
這樣計劃任務就建立好了。每天上午4點,計算機自動執行 ok-ef 。
3. 修改計劃任務
基礎用法
使用 Win + R 執行 taskschd.msc 開啟計劃任務程式,右擊上述計劃任務。
- 點選
运行可以手動執行計劃任務。 - 點選
停止可以停止正在執行的計劃任務。注意 ok-ef 是非阻塞執行,需要手動關閉。 - 點選
禁用可以禁止計劃任務自動執行。 - 點選
启用可以允許計劃任務自動執行。
修改執行時間和頻率
使用 Win + R 執行 taskschd.msc 開啟計劃任務程式,雙擊上述計劃任務,進入詳情頁,切換到 触发器 ,可以修改執行時間和頻率。
在另一個計劃任務結束後執行
如果希望 ok-ef 在另一個計劃任務(比如 ok-ww)結束後執行,而不是定時執行。可以使用 自定义触发器 。
使用 Win + R 執行 taskschd.msc 開啟計劃任務程式,雙擊上述計劃任務,進入詳情頁,切換到 触发器 。
點選 新建,在 开始任务 選擇 发生时间时 。點選 自定义 和 新建事件选择器 。
在彈出的視窗中,切換到 XML 。勾選 手动编辑查询,在文本框中貼入:
<QueryList>
<Query Id="0" Path="Microsoft-Windows-TaskScheduler/Operational">
<Select Path="Microsoft-Windows-TaskScheduler/Operational">*[System[(EventID=102)]] and *[EventData[Data[@Name='TaskName'] and (Data='PLACEHOLDER_TASK_PATH')]]</Select>
</Query>
</QueryList>
將 PLACEHOLDER_TASK_PATH 替換成前序任務的路徑(可以在對應計劃任務的詳情頁 常规 中找到,是 位置 和 名称 用反斜槓拼接)。
一路點選 确定 關閉所有彈出視窗。